#!/usr/bin/env bash
# 这是一个命令模块，可以通过名字或路径搜索执行已经注册的命令或者已经注册路径下的命令。
# 一个文件中只有一个命令，一个命令至少有四部分组成：
# 1.命令的名字变量  COMMAND_NAME
# 2.命令的主函数    command_main
# 3.命令的退出函数  command_exit
# 4.命令的帮助函数  command_help
# 以内置命令清屏为例：
:<<!
#!/usr/bin/env bash

# 命令的名字。
COMMAND_NAME="clear"

# 主函数，当命令被调用时执行。
command_main()
{
    pushtop
}

# 退出，在command_main结束后被调用。
# 一般不要在这个函数里进行耗时的操作，即使command_main被打断，系统退出前依然会执行这个函数。
command_exit()
{
    return 0
}

# 帮助信息，在help或者cmd --help时被调用。
command_help()
{
    help_print "clear" "clear the terminal screen"
    return 0
}
!

# 已经注册的路径
_COMMAND_DIR_LIST__=();
# 已经注册的文件
_COMMAND_FILE_LIST__=();

# 取消命令加载
unset_cmd()
{
    unset command_exit
    unset command_do
    COMMAND_NAME=""
}
# 递归遍历注册的路径下的所有文件，打印符合命令文件格式的文件的路径。
command_travel_dir()
{
    for path in $1/*; do
        if [[ -f $path && $path == *".sh" ]]; then
            unset_cmd
            source $path
            [[ -z $COMMAND_NAME ]] || echo "$path"
        fi
        [[ -d $path && $path != "."* ]] && command_travel_dir $path
    done
}

# 打印所有已经注册的命令文件的地址
command_travel()
{
    for path in ${_COMMAND_FILE_LIST__[@]}; do
        if [[ -f $path && $path == *".sh" ]]; then
           unset_cmd
            source $path
            [[ -z $COMMAND_NAME ]] || echo "$path"
        fi
    done
    for path in ${_COMMAND_DIR_LIST__[@]}; do
        [[ -d $path && $path != "."* ]] && command_travel_dir $path
    done
    unset_cmd
}

# 打印所有已经注册的命令的名字
command_name_travel()
{
    cmdpaths="$(command_travel)"
    while IFS=$'\n' read -r line; do
        unset_cmd
        source $line
        [[ -z $COMMAND_NAME ]] || echo "$COMMAND_NAME"
    done <<< "$cmdpaths"
}

# 在注册的所有路径下的所有文件中搜索一个命令名字，并打印其地址。
command_search_dir()
{
    dir_string=$1
    cmd_string=$2
    for path in $dir_string/*.sh; do
        if [[ -f $path ]]; then
            unset_cmd
            source $path
            if [[ $COMMAND_NAME == $cmd_string ]]; then
                echo "$path"
                unset_cmd
                return 0
            fi 
        fi
    done
    for path in $dir_string/*; do
        if [[ -d $path ]]; then
            command_search_dir $path $cmd_string && return 0
        fi
    done
    unset_cmd
    return 1
}

# 在所有注册的命令中查找一个命令，并打印其地址。
command_search()
{
    cmd_string="$*"
    [[ -z $cmd_string ]] && return 1
    for path in ${_COMMAND_FILE_LIST__[@]}; do
        unset_cmd
        source $path
        if [[ $COMMAND_NAME == $cmd_string ]]; then
            echo "$path"
            unset_cmd
            return 0
        fi 
    done
    path=""
    for path in ${_COMMAND_DIR_LIST__[@]}; do
        command_search_dir $path $cmd_string && return 0
    done
    unset_cmd
    return 1
}

# 通过地址执行命令，第一个参数是命令的地址，后面的参数是传递给该命令的参数。
command_exec()
{
    path=$1
    params=${@:2:$#-1}
    if [[ ! -f $path ]]; then
        debug_echo "Commamd path does not exist! $path."
        return 1
    fi
    unset_cmd
    source $path
    IFS=$' '
    if ! command_main $params; then
        error_echo "Command Exec Error!"
        debug_echo "Command exec error! Func:\"$COMMAND_NAME\" Params:\"$params\" Path:\"$path\"."
        unset_cmd
        return 1
    fi
    if ! command_exit; then
        error_echo "Command Exit Error!" 
        debug_echo "Command exit error! Func:\"$COMMAND_NAME\" Path:\"$path\"."
        unset_cmd
        return 1
    fi
    unset_cmd
    return 0
}

# 通过名字或地址执行命令，第一个参数是命令的名字或地址，后面的参数是传递给该命令的参数。
DO_CMD()
{
    cmd_string="$1"
    params=${@:2:$#-1}
    [[ $cmd_string == "@" ]] && return 0
    [[ $cmd_string == "" ]] && return 1
    if [[ ${!#} == "--help" ]]; then
        cmd_string="help"
        params=${@:1:$#-1}
    fi
    if [[ ${!#} == "--version" ]]; then
        cmd_string="version"
        params=${@:1:$#-1}
    fi
    # debug_echo "Do cmd $cmd_string $params"
    cd $RootDir >/dev/null
    set_cmd_status "$CMD_STATUS_RUNNING"
    if [[ -f $cmd_string ]]; then
        if [[ $cmd_string == *".sh" ]]; then
            if ! command_exec $cmd_string; then
                set_cmd_status "$CMD_STATUS_FAILD"
                return $CMD_STATUS_FAILD
            fi
        else
            debug_echo "The file suffix needs to use \".sh\""
            set_cmd_status "$CMD_STATUS_FAILD"
            return $CMD_STATUS_FAILD
        fi
    else
        if r_cmd_string="$(command_search $cmd_string)"; then
            if ! command_exec $r_cmd_string $params; then
                set_cmd_status "$CMD_STATUS_FAILD"
                return $CMD_STATUS_FAILD
            fi
        else
            error_echo "The \"$cmd_string\" command was not found!"
            set_cmd_status "$CMD_STATUS_NOFOUND"
            return $CMD_STATUS_NOFOUND
        fi
    fi
    cd - >/dev/null
    set_cmd_status "$CMD_STATUS_SUCCESS"
    return $CMD_STATUS_SUCCESS
}

# 通过脚本文件执行命令，每个命令占一行，支持忽略空行和开头是'#'或者‘//’的注释信息。
DO_CMD_BY_FILE()
{
    [[ ! -e "$1" ]] && error_echo "The file \"$1\" does not exist!" && return 1
    while IFS=$'\n' read -r line; do
        [[ -z "$line" || "$line" == "#"* || "$line" =~ "//"* ]] && continue
        DO_CMD $line || return 1
    done < "$1"
    return 0
}

# 注册一个命令的地址或一个或多个命令的路径。
command_regist()
{
    path=$1
    [[ -z $path ]] && return 1
    for o_dir in ${_COMMAND_DIR_LIST__[@]}; do
        [[ $o_dir == "$path"* ]] && debug_echo "Registered an existing file:${path}" &&return 1
    done
    if [[ -d $path ]];then
        for inx in {1..${#_COMMAND_DIR_LIST__[@]}}; do
            if [[ $_COMMAND_DIR_LIST__[$inx] == "${path}"* ]]; then
                debug_echo "Updated a command file:$_COMMAND_DIR_LIST__[$inx] >> ${path}"
                _COMMAND_DIR_LIST__[$inx]=("${path}")
                return 0
            fi
        done
        debug_echo "Registered a command dir:${path}"
        _COMMAND_DIR_LIST__+=("${path}")
        return 0
    elif [[ -f $path ]];then
        for o_file in ${_COMMAND_FILE_LIST__[@]}; do
            [[ $o_file == "$path" ]] && return 1
        done
        debug_echo "Registered a command file:${path}"
        _COMMAND_FILE_LIST__+=("${path}")
        return 0
    fi
    return 1
}

# 设置当前命令的执行命令的状态。
# 正在运行：CMD_STATUS_RUNNING
# 没有找到：CMD_STATUS_NOFOUND
# 执行失败：CMD_STATUS_FAILD
# 执行成功：CMD_STATUS_SUCCESS
set_cmd_status()
{
    echo -n "$1" >$CMD_STATUS_FILE
}
# 读取当前命令的执行状态。
read_cmd_status()
{
    read_file $CMD_STATUS_FILE
}
module_init()
{
    export CMD_STATUS_RUNNING=6
    export CMD_STATUS_NOFOUND=2
    export CMD_STATUS_FAILD=1
    export CMD_STATUS_SUCCESS=0
    export CMD_TMP_DIR="${TmpDir}/module/command"
    export CMD_STATUS_FILE="${CMD_TMP_DIR}/status"
    [[ -d ${CMD_TMP_DIR} ]] || mkdir -p ${CMD_TMP_DIR}
    set_cmd_status "$CMD_STATUS_SUCCESS"
    # 内置命令的路径
    export InCmdDir="${CoreDir}/command"
    # 注册所有内置命令
    command_regist "${InCmdDir}"
    
    debug_echo "Command module is ok!"
    return 0
}

module_exit()
{
    [ "$(type -t command_exit)" = "function" ] && command_exit
    unset_cmd
    rm -fr ${CMD_TMP_DIR}
    unset CMD_STATUS_RUNNING
    unset CMD_STATUS_NOFOUND
    unset CMD_STATUS_FAILD
    unset CMD_STATUS_SUCCESS
    unset CMD_TMP_DIR
    unset CMD_STATUS_FILE
    unset InCmdDir
    debug_echo "Command module exited!"
    return 0
}